package com.example.demo.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.example.demo.base.config.auth.UserInfoShareHolder;
import com.example.demo.base.login.dto.UserCache;
import com.example.demo.domain.PageResult;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.entity.VojSubmissions;
import com.example.demo.domain.exam.Exam;
import com.example.demo.domain.exam.ExamClassRelation;
import com.example.demo.domain.exam.ExamPaper;
import com.example.demo.domain.exam.ExamSubmitLog;
import com.example.demo.domain.exam.ExamUserDelay;
import com.example.demo.domain.exam.ExamUserScore;
import com.example.demo.domain.exam.ExamUserSingleQuesScore;
import com.example.demo.domain.exam.QueryModel;
import com.example.demo.domain.exam.UserExamAns;
import com.example.demo.domain.exam.condition.ExamCondition;
import com.example.demo.dto.ProgrammingQuestionDTO;
import com.example.demo.dto.UserExamAnswerDTO;
import com.example.demo.enums.ExamScoreStatus;
import com.example.demo.enums.ExamStatus;
import com.example.demo.enums.PaperType;
import com.example.demo.enums.QuestionType;
import com.example.demo.enums.SubmitType;
import com.example.demo.exception.ExceptionUtils;
import com.example.demo.mapper.ExamMapper;
import com.example.demo.param.voj.SubmissionCreateParam;
import com.example.demo.service.ExamForUserService;
import com.example.demo.service.UserService;
import com.example.demo.service.VojSubmissionsService;
import com.example.demo.service.exam.ExamClassRelationService;
import com.example.demo.service.exam.ExamPaperService;
import com.example.demo.service.exam.ExamService;
import com.example.demo.service.exam.ExamSubmitLogService;
import com.example.demo.service.exam.ExamUserDelayService;
import com.example.demo.service.exam.ExamUserScoreService;
import com.example.demo.service.exam.ExamUserSingleQuesScoreService;
import com.example.demo.service.exam.UserExamAnswerService;
import com.example.demo.task.PeopleCountTask;
import com.example.demo.utils.GetIpAddrUtil;
import com.example.demo.utils.JsonUtils;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
public class ExamForUserServiceImpl implements ExamForUserService {
    private final String USER_ANSWER_STAGING_HASH = "__answer";

    private final static Long ONE_MINUTES = 60 * 1000L;

    private final ExamService examService;

    private final ExamPaperService examPaperService;

    private final ExamClassRelationService examClassRelationService;

    private final UserExamAnswerService userExamAnswerService;

    private final ExamUserScoreService examUserScoreService;

    private final RedisTemplate<Object, Object> redisTemplate;

    private final ExamSubmitLogService examSubmitLogService;

    private final ExamUserSingleQuesScoreService examUserSingleQuesScoreService;

    private final ExamUserDelayService examUserDelayService;

    private final UserService userService;

    private final VojSubmissionsService vojSubmissionsService;



    @Override
    public PageResult<Exam> page(QueryModel<ExamCondition> model) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        User user = userInfo.getUser();
        Long userId = user.getId();
        ExamCondition condition = Optional.ofNullable(model.getCondition()).orElse(new ExamCondition());
        List<Long> classIds = examClassRelationService.getExamClassRelationsByUserId(userId).stream().map(ExamClassRelation::getClassId).collect(Collectors.toList());
        condition.setClassIds(classIds);
        model.setCondition(condition);
        PageResult<Exam> result = examService.pageForUser(model);
        List<Long> examList = result.getData().stream().map(Exam::getId).collect(Collectors.toList());
        Map<Long, ExamUserScore> map = examUserScoreService.getByExamUserId(examList, userId)
                .stream().collect(Collectors.toMap(ExamUserScore::getExamId, e -> e));
        result.getData().forEach(e -> {
            ExamUserScore examUserScore = map.get(e.getId());
            examUserScore.setType(Optional.ofNullable(examUserScore.getType()).orElse(SubmitType.NOT_SUBMIT));
            e.setUserScore(examUserScore);
        });
        return result;
    }

    @Override
    public ExamPaper getExamDetail(Long examId, HttpServletRequest request) {
        if (examId == null) {
            throw ExceptionUtils.exception("考试标识不能为空");
        }
        // TODO 校验学生是否属于该场考试的人员
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        User user = userInfo.getUser();
        Long userId = user.getId();
        Exam exam = Optional.ofNullable(examService.getExamById(examId)).orElseThrow(() -> ExceptionUtils.exception("当前考试不存在"));
        ExamUserScore examUserScore = Optional.ofNullable(examUserScoreService.getByExamUserId(examId, userId)).orElse(new ExamUserScore());
        examUserScore.setType(Optional.ofNullable(examUserScore.getType()).orElse(SubmitType.NOT_SUBMIT));
        if (examUserScore.getType()!=SubmitType.NOT_SUBMIT ){
            throw ExceptionUtils.exception("用户已交卷，无法再次进入考试");
        }
        if (!examClassRelationService.existRelation(userId, exam.getClassId())) {
            throw ExceptionUtils.exception("无法进入该场考试，您不属于该班级");
        }
        // 校验Ip地址
        validIp(user, request, examUserScore.getIp());
        // 统计考试人数
        numberIncreasing(examId, exam.getCompanyId());
        examUserScore.setEnterTime(Optional.ofNullable(examUserScore.getEnterTime()).orElse(new Timestamp(System.currentTimeMillis())));
        examUserScore.setStatus(ExamScoreStatus.NOT_STARTED);
        examUserScore.setIp(Optional.ofNullable(examUserScore.getIp()).orElse(userInfo.getIp()));
        examUserScore.setExamId(examId);
        examUserScore.setExamName(exam.getExamName());
        examUserScore.setUserId(userId);
        examUserScore.setUsername(user.getUsername());
        examUserScore.setClassId(Optional.ofNullable(examUserScore.getClassId()).orElse(exam.getClassId()));
        // examUserScore.setType(SubmitType.NOT_SUBMIT);
        ExamUserDelay delay = validExamTime(exam, examUserScore, userId);
        ExamPaper examPaperDetail = examPaperService.getExamPaperDetail(exam.getPaperId(), PaperType.ALL_QUESTIONS);
        examPaperDetail.setDelayTime(delay.getDelayTime());
        examPaperDetail.getObjectiveQuestions().forEach(e -> e.setAnswers(null));
        examPaperDetail.setFirstEnterTime(examUserScore.getEnterTime());
        examPaperDetail.setStartTime(exam.getStartTime());
        examPaperDetail.setEndTime(exam.getEndTime());
        examPaperDetail.setDurationTime(exam.getDurationTime());
        examPaperDetail.setLanguageType(String.valueOf(exam.getLanguageType()));
        examUserScoreService.insertOrUpdate(examUserScore);

        // 更新exam数据库中实到人数 +1
        examService.updateExamAttendNum(examId);
        return examPaperDetail;
    }

    private void numberIncreasing(Long examId, Long companyId) {
        HashOperations<Object, Object, Object> ops = redisTemplate.opsForHash();
        String hashKey = companyId + "__" + examId;
        if (!ops.hasKey(PeopleCountTask.STRING_PEOPLE_COUNT, hashKey)) {
            redisTemplate.opsForHash().put(PeopleCountTask.STRING_PEOPLE_COUNT, hashKey, 0);
        }
        redisTemplate.opsForHash().increment(PeopleCountTask.STRING_PEOPLE_COUNT, companyId + "__" + examId, 1);
    }

    @Override
    @SuppressWarnings("all")
    public void stagingExamAns(UserExamAnswerDTO dto) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        User user = userInfo.getUser();
        Long userId = user.getId();
        List<UserExamAns> list = dto.getList();
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        Long examId = Optional.ofNullable(dto.getExamId()).orElseThrow(() -> ExceptionUtils.exception("考试标识不能为空"));
        Long paperId = Optional.ofNullable(dto.getPaperId()).orElseThrow(() -> ExceptionUtils.exception("试卷标识不能为空"));
        Exam exam = Optional.ofNullable(examService.getExamById(examId)).orElseThrow(() -> ExceptionUtils.exception("当前考试不存在"));
        ExamUserScore examUserScore = Optional.ofNullable(examUserScoreService.getByExamUserId(examId, userId)).orElseThrow(() -> ExceptionUtils.exception("考试成绩不存在"));
        validStagingTime(exam, examUserScore, userId);
        list.forEach(e -> checkExamAnswer(e, userId, examId, paperId));
        String json = JsonUtils.serialize(list);
        redisTemplate.opsForHash().put(USER_ANSWER_STAGING_HASH, examId + "" + userId, json);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitExamAns(UserExamAnswerDTO dto) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        User user = userInfo.getUser();
        Long userId = user.getId();
        Long examId = Optional.ofNullable(dto.getExamId()).orElseThrow(() -> ExceptionUtils.exception("考试标识不能为空"));
        Long paperId = Optional.ofNullable(dto.getPaperId()).orElseThrow(() -> ExceptionUtils.exception("考试标识不能为空"));
        Exam exam = Optional.ofNullable(examService.getExamById(examId)).orElseThrow(() -> ExceptionUtils.exception("当前考试不存在"));
        ExamUserScore examUserScore = examUserScoreService.getByExamUserId(examId, userId);
        validSubmitTime(exam, examUserScore, userId);
        if (examUserScore.getType() != SubmitType.NOT_SUBMIT) {
            throw ExceptionUtils.exception("已交卷无法再次提交");
        }
        List<UserExamAns> list = (CollectionUtils.isEmpty(dto.getList()) ? Optional.ofNullable(JsonUtils.deserializeList((String) redisTemplate.opsForHash()
                .get(USER_ANSWER_STAGING_HASH, examId + "" + userId), UserExamAns.class)).orElse(Collections.emptyList()) : dto.getList())
                .stream().filter(e -> e.getQuestionType() != QuestionType.PROGRAMMING_QUESTION).collect(Collectors.toList());
        list.forEach(e -> checkExamAnswer(e, userId, examId, paperId));
        userExamAnswerService.insertList(list);
        List<ExamUserSingleQuesScore> scores = new ArrayList<>();
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        list.forEach(e -> {
            ExamUserSingleQuesScore singleQuesScore = new ExamUserSingleQuesScore();
            singleQuesScore.setPaperId(paperId);
            singleQuesScore.setCreateTime(timestamp);
            singleQuesScore.setClassId(exam.getClassId());
            singleQuesScore.setUserId(userId);
            singleQuesScore.setExamId(examId);
            singleQuesScore.setQuestionId(e.getQuestionId());
            singleQuesScore.setQuestionType(e.getQuestionType());
            scores.add(singleQuesScore);
        });
        examUserSingleQuesScoreService.insertBatch(scores);
        examUserScore.setType(Optional.ofNullable(dto.getType()).orElse(SubmitType.AUTO_SUBMIT));
        examUserScore.setSubmitTime(new Timestamp(System.currentTimeMillis()));
        examUserScore.setTime((examUserScore.getSubmitTime().getTime() - examUserScore.getEnterTime().getTime() / ONE_MINUTES));
        examUserScoreService.insertOrUpdate(examUserScore);
    }

    private void checkExamAnswer(UserExamAns entity, Long userId, Long examId, Long paperId) {
        if (entity.getQuestionId() == null) {
            throw ExceptionUtils.exception("题目标识不能为空");
        }
        if (entity.getQuestionType() == null) {
            throw ExceptionUtils.exception("题目类型不能为空");
        }
        if (entity.getQuestionType() != QuestionType.PROGRAMMING_QUESTION && CollectionUtils.isEmpty(entity.getOptions())) {
            throw ExceptionUtils.exception("选项不能为空");
        }
        entity.setCreateTime(new Timestamp(System.currentTimeMillis()));
        entity.setUserId(userId);
        entity.setExamId(examId);
        entity.setPaperId(paperId);
    }

    @Override
    public List<UserExamAns> getStagingExamAns(Long examId) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        User user = userInfo.getUser();
        Long userId = user.getId();
        if (examId == null) {
            throw ExceptionUtils.exception("考试标识不能为空");
        }
        return Optional.ofNullable(JsonUtils.deserializeList((String) redisTemplate.opsForHash()
                .get(USER_ANSWER_STAGING_HASH, examId + "" + userId), UserExamAns.class)).orElse(Collections.emptyList());
    }

    @Override
    public List<UserExamAns> getExamAns(Long examId) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        User user = userInfo.getUser();
        Long userId = user.getId();
        if (examId == null) {
            throw ExceptionUtils.exception("考试标识不能为空");
        }
        return userExamAnswerService.getExamAns(examId, userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitExamLog(ExamSubmitLog entity) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        User user = userInfo.getUser();
        // 保存用户提交记录
        this.saveSubmitLog(entity, user);
        // 更新或保存用户用户答案实体、单道题实体
        this.saveUserExamAnswer(entity, user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSubmissionLog(Long submissionId, double score, Integer status
            , VojSubmissions submissions) {
        VojSubmissions submission = Optional.ofNullable(submissions).orElse(new VojSubmissions());
        ExamSubmitLog submitLog = Optional.ofNullable(examSubmitLogService.getSubmitLogBySubmissionId(submissionId))
                .orElseThrow(() -> ExceptionUtils.exception("未生成对应的提交记录"));
        ExamPaper paper = examPaperService.getExamPaperWithOnlyProgrammingQuestion(submitLog.getPaperId());
        Integer totalScore = paper.getTotalScore();
        Map<Long, Double> map = paper.getProgrammingQuestions().stream().collect(Collectors.toMap(ProgrammingQuestionDTO::getProblemId, ProgrammingQuestionDTO::getPercentage));
        double finalScore = score / 100 * totalScore * map.getOrDefault(submitLog.getQuestionId(), 1d);
        // ExamUserSingleQuesScore 改成 List<ExamUserSingleQuesScore>
        List<ExamUserSingleQuesScore> singleQuesScore = examUserSingleQuesScoreService.getByUserExamQuestionId(submitLog.getUserId(), submitLog.getExamId(), submitLog.getQuestionId());
        // 最后一次提交结果设置最终分
        singleQuesScore.get(singleQuesScore.size()-1).setScore(finalScore);
        submitLog.setScore(finalScore);
        submitLog.setStatus(status);
        submitLog.setExecuteTime(submission.getSubmissionUsedTime());
        submitLog.setUserMemory(submission.getSubmissionUsedMemory());
        submitLog.setJudgeLog(submission.getSubmissionJudgeLog());
        submitLog.setJudgeResult(submission.getSubmissionJudgeResult());
        System.out.println("update更新： "+ submitLog);
        examUserSingleQuesScoreService.insertOrUpdate(singleQuesScore.get(singleQuesScore.size()-1));

        examSubmitLogService.update(submitLog);
    }
    // 提交代码
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long submitEvaluation(ExamSubmitLog entity) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        User user = userInfo.getUser();
        // 更新或保存用户用户答案实体、单道题实体
        this.saveUserExamAnswer(entity, user);
        SubmissionCreateParam param = new SubmissionCreateParam();
        param.setProblemId(entity.getQuestionId());
        param.setLanguageId(entity.getLanguageId());
        param.setCode(entity.getContent());
        param.setSourceModule("exam");
        Long submissionId = vojSubmissionsService.createSubmission(param);
        entity.setSubmissionId(submissionId);
        // 保存用户提交记录
        this.saveSubmitLog(entity, user);
        return submissionId;
    }

    private void saveSubmitLog(ExamSubmitLog entity, User user) {
        entity.setUserId(user.getId());
        entity.setUsername(user.getUsername());
        examSubmitLogService.insert(entity);
    }

    private void saveUserExamAnswer(ExamSubmitLog entity, User user) {
        Long userId = user.getId();
        Long examId = entity.getExamId();
        Long questionId = entity.getQuestionId();
        UserExamAns answer = Optional.ofNullable(userExamAnswerService.getByExamUserQuestionId(examId, userId, questionId)).orElse(new UserExamAns());
        answer.setUserId(userId);
        answer.setExamId(entity.getExamId());
        answer.setContent(entity.getContent());
        answer.setPaperId(entity.getPaperId());
        answer.setCreateTime(entity.getSubmitTime());
        answer.setQuestionType(QuestionType.PROGRAMMING_QUESTION);
        answer.setQuestionId(entity.getQuestionId());
        userExamAnswerService.saveOrUpdateProgrammingQuestion(answer);
        this.saveSingleQuestionScore(Optional.ofNullable(answer.getSingleQuesScore()).orElse(new ExamUserSingleQuesScore()), entity, user.getId());
    }

    private void saveSingleQuestionScore(ExamUserSingleQuesScore singleQuesScore, ExamSubmitLog entity, Long userId) {
//        ExamPaper paper = examPaperService.getExamPaperWithOnlyProgrammingQuestion(entity.getPaperId());
//        Integer totalScore = paper.getTotalScore();
//        Map<Long, Double> map = paper.getProgrammingQuestions().stream().collect(Collectors.toMap(ProgrammingQuestionDTO::getProblemId, ProgrammingQuestionDTO::getPercentage));
        singleQuesScore.setPaperId(entity.getPaperId());
        // singleQuesScore.setScore(totalScore * map.getOrDefault(entity.getQuestionId(), 1d) * entity.getScore());
        singleQuesScore.setCreateTime(entity.getSubmitTime());
        singleQuesScore.setClassId(entity.getClassId());
        singleQuesScore.setUserId(userId);
        singleQuesScore.setExamId(entity.getExamId());
        singleQuesScore.setQuestionId(entity.getQuestionId());
        singleQuesScore.setQuestionType(QuestionType.PROGRAMMING_QUESTION);
        examUserSingleQuesScoreService.insertOrUpdate(singleQuesScore);
    }

    private void validSubmitLog(ExamSubmitLog entity) {
        Optional.ofNullable(entity.getClassId()).orElseThrow(() -> ExceptionUtils.exception("班级标识不能为空"));
        Optional.ofNullable(entity.getExamId()).orElseThrow(() -> ExceptionUtils.exception("考试标识不能为空"));
        Optional.ofNullable(entity.getPaperId()).orElseThrow(() -> ExceptionUtils.exception("试卷标识不能为空"));
        Optional.ofNullable(entity.getQuestionId()).orElseThrow(() -> ExceptionUtils.exception("问题标识不能为空"));
        Optional.ofNullable(entity.getSubmitTime()).orElseThrow(() -> ExceptionUtils.exception("提交时间不能为空"));
        Optional.ofNullable(entity.getStartTime()).orElseThrow(() -> ExceptionUtils.exception("考试开始不能为空"));
        Optional.ofNullable(entity.getType()).orElseThrow(() -> ExceptionUtils.exception("评测语言不能为空"));
        // Optional.ofNullable(entity.getScore()).orElseThrow(() -> ExceptionUtils.exception("评测结果得分不能为空"));
    }

    private void validSubmitTime(Exam exam, ExamUserScore examUserScore, Long userId) {
        ExamStatus examStatus = exam.getExamStatus();
        Timestamp currentTime = new Timestamp(System.currentTimeMillis());
        Timestamp startTime = exam.getStartTime();
        Timestamp endTime = exam.getEndTime();
        Timestamp enterTime = examUserScore.getEnterTime();
        if (examStatus == ExamStatus.PRE_EXAM || currentTime.before(startTime)) {
            throw ExceptionUtils.exception("考试还未开始，无法提交答案");
        }
        ExamUserDelay delay = Optional.ofNullable(examUserDelayService.getByExamUserId(exam.getId(), userId)).orElse(new ExamUserDelay());
        Integer delayTime = Optional.ofNullable(delay.getDelayTime()).orElse(0);
        long diffTime = (currentTime.getTime() - enterTime.getTime()) - (endTime.getTime() + delayTime * ONE_MINUTES - startTime.getTime());
        if (diffTime > 0) {
            throw ExceptionUtils.exception("考试已超时，无法提交答案");
        }
        if (currentTime.after(endTime) || examStatus == ExamStatus.OVER) {
            throw ExceptionUtils.exception("考试已结束，无法提交答案");
        }
    }

    private ExamUserDelay validExamTime(Exam exam, ExamUserScore examUserScore, Long userId) {

        Timestamp currentTime = new Timestamp(System.currentTimeMillis());
        Timestamp startTime = exam.getStartTime();
        Timestamp endTime = exam.getEndTime();
        ExamStatus examStatus = exam.getExamStatus();
        Timestamp enterTime = examUserScore.getEnterTime();
        if (examStatus == ExamStatus.PRE_EXAM || currentTime.before(startTime)) {
            throw ExceptionUtils.exception("考试还未开始，无法进入考试");
        }
        if (examUserScore.getType() != SubmitType.NOT_SUBMIT) {
            throw ExceptionUtils.exception("已交卷无法进入考试");
        }
        ExamUserDelay delay = Optional.ofNullable(examUserDelayService.getByExamUserId(exam.getId(), userId)).orElse(new ExamUserDelay());
        Integer delayTime = Optional.ofNullable(delay.getDelayTime()).orElse(0);
        if (enterTime != null) {
            long diffTime = (currentTime.getTime() - enterTime.getTime()) - (endTime.getTime() + delayTime * ONE_MINUTES - startTime.getTime());
            if (diffTime > 0) {
                throw ExceptionUtils.exception("考试已超时，无法提交答案");
            }
        }
        if (currentTime.after(endTime) || examStatus == ExamStatus.OVER) {
            throw ExceptionUtils.exception("考试已结束，无法提交答案");
        }
        return delay;
    }

    private void validIp(User user, HttpServletRequest request, String ip) {
        if (!userService.isIdentity(user.getId())) {
            throw ExceptionUtils.exception("用户未实名，无法进入考试");
        }
        if (StringUtils.isBlank(ip)) {
            return;
        }
        String ipAddr = GetIpAddrUtil.getIpAddr(request);
        if (!StringUtils.equalsIgnoreCase(ipAddr, ip)) {
            throw ExceptionUtils.exception("ip地址已锁定无法进入，请使用第一次登录到系统的机器");
        }
    }
    private void validStagingTime(Exam exam, ExamUserScore examUserScore, Long userId) {
        ExamStatus examStatus = exam.getExamStatus();
        Timestamp currentTime = new Timestamp(System.currentTimeMillis());
        Timestamp startTime = exam.getStartTime();
        Timestamp endTime = exam.getEndTime();
        Timestamp enterTime = examUserScore.getEnterTime();
        if (examStatus == ExamStatus.PRE_EXAM || currentTime.before(startTime)) {
            throw ExceptionUtils.exception("考试还未开始，无法暂存答案");
        }
        ExamUserDelay delay = Optional.ofNullable(examUserDelayService.getByExamUserId(exam.getId(), userId)).orElse(new ExamUserDelay());
        Integer delayTime = Optional.ofNullable(delay.getDelayTime()).orElse(0);
        long diffTime = (currentTime.getTime() - enterTime.getTime()) - (endTime.getTime() + delayTime * ONE_MINUTES - startTime.getTime());
        if (diffTime > 0) {
            throw ExceptionUtils.exception("考试已超时，无法暂存答案");
        }
        if (currentTime.after(endTime) || examStatus == ExamStatus.OVER) {
            throw ExceptionUtils.exception("考试已结束，无法暂存答案");
        }
    }

}
